package com.xtt._03_NIO;

import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

/**
 * @Author xtt.
 * @Description: Selector:单线层可以处理多个channel
 * @Date:Create in 2017/9/22 0022 下午 22:31
 * @Modify By:
 */
public class SelectorTest {
    /**
     * 客户端
     */
    public static void main(String[] args) throws IOException {
        //1. 获取socketChannel
        SocketChannel sChannel = SocketChannel.open();
        //2. 创建连接
        sChannel.connect(new InetSocketAddress("127.0.0.1", 9898));
        ByteBuffer buf = ByteBuffer.allocate(1024);
        //3. 设置通道为非阻塞
        sChannel.configureBlocking(false);

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String msg = scanner.nextLine();

            buf.put((new Date() + "：" + msg).getBytes());
            buf.flip();
            //4. 向通道写数据
            sChannel.write(buf);
            buf.clear();
        }
    }

    /**
     * 服务端
     */
    @Test
    public void server() throws IOException {
        //1.获取服务端通道
        ServerSocketChannel server = ServerSocketChannel.open();
        server.bind(new InetSocketAddress(9898));
        //2.设置为非阻塞模式（fileChannel不能设置为非阻塞模式）
        server.configureBlocking(false);

        //3.打开一个监听器
        Selector selector = Selector.open();
        //4.向监听器注册接收事件
            /*读 : SelectionKey.OP_READ （1）
        　　  写 : SelectionKey.OP_WRITE （4）
        　　  连接 : SelectionKey.OP_CONNECT （8）
        　　  接收 : SelectionKey.OP_ACCEPT （16）*/
        server.register(selector, SelectionKey.OP_ACCEPT);
        //selector.select(),阻塞到至少有一个通道在你注册的事件上就绪了。返回int 距上一次select()期间就绪的数量
        //select(long timeout)和select()一样，除了最长会阻塞timeout毫秒(参数)。
        //selectNow()不会阻塞，不管什么通道就绪都立刻返回，没有则为0
        while(selector.select()>0) {
            //5.获取监听器上所有监听的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                //6.获取SelectionKey
                SelectionKey key = iterator.next();
                //7.根据key判断对应的事件
                if(key.isAcceptable()) {
                    //8.处理服务端接收到的接入请求事件
                    SocketChannel accept = server.accept();
                    accept.configureBlocking(false);
                    accept.register(selector,SelectionKey.OP_READ);//
                }else if(key.isReadable()) {
                    //9.处理客户端发出的读取事件
                    SocketChannel channel = (SocketChannel)key.channel();
                    readMsg(channel);
                }
                //每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。
                //下次该通道变成就绪时，Selector会再次将其放入已选择键集中。
                iterator.remove();
            }
        }
    }

    private void readMsg(SocketChannel channel) throws IOException {
        ByteBuffer buf = ByteBuffer.allocate(1024);
        int len = 0;
        while ((len = channel.read(buf)) > 0) {
            buf.flip();
            byte[] bytes = new byte[1024];
            buf.get(bytes, 0, len);
            System.out.println(new String(bytes, 0, len));
        }
    }
}
